<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>AI Assistant</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/github-markdown-light.min.css') }}">
    <style>
        .chat-container {
            max-width: 1200px;
            margin: 20px auto;
            padding: 20px;
        }
        .chat-header {
            margin-bottom: 20px;
        }
        .message-container {
            height: calc(100vh - 280px);
            overflow-y: auto;
            padding: 20px;
            border: 1px solid #ebeef5;
            border-radius: 4px;
            margin-bottom: 20px;
        }
        .message {
            margin-bottom: 20px;
            display: flex;
            align-items: flex-start;
        }
        .message-content {
            max-width: 80%;
            padding: 12px 16px;
            border-radius: 8px;
            margin-left: 12px;
            line-height: 1.6;
            font-size: 14px;
            color: #2c3e50;
        }
        .message-content pre {
            background-color: #f6f8fa;
            border-radius: 6px;
            padding: 16px;
            overflow: auto;
            font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
            font-size: 85%;
            line-height: 1.45;
            margin: 1em 0;
        }
        .message-content code {
            background-color: rgba(175,184,193,0.2);
            border-radius: 4px;
            padding: 0.2em 0.4em;
            font-family: monospace;
        }
        .message-content p {
            margin: 0.5em 0;
        }
        .message-content ul, 
        .message-content ol {
            padding-left: 2em;
            margin: 1em 0;
        }
        .message-content table {
            display: block;
            border-collapse: collapse;
            width: 100%;
            overflow: auto;
            margin: 1em 0;
        }
        .message-content th,
        .message-content td {
            border: 1px solid #dfe2e5;
            padding: 6px 13px;
        }
        .message-content th {
            font-weight: 600;
            background-color: #f6f8fa;
        }
        .message-content tr:nth-child(2n) {
            background-color: #f6f8fa;
        }
        .message-content img {
            max-width: 100%;
            height: auto;
        }
        .message-content h1,
        .message-content h2,
        .message-content h3,
        .message-content h4 {
            margin: 1em 0 0.5em;
            font-weight: 600;
            line-height: 1.25;
        }
        .message-content h1 { font-size: 2em; }
        .message-content h2 { font-size: 1.5em; }
        .message-content h3 { font-size: 1.25em; }
        .message-content h4 { font-size: 1em; }
        .message-content blockquote {
            padding: 0 1em;
            color: #6a737d;
            border-left: 0.25em solid #dfe2e5;
            margin: 1em 0;
        }
        .user-message .message-content {
            background-color: #ecf5ff;
        }
        .assistant-message .message-content {
            background-color: #f4f4f5;
        }
        .input-area {
            display: flex;
            gap: 10px;
        }
        .typing {
            opacity: 0.5;
        }
        .stop-generating {
            margin-left: 10px;
        }
    </style>
    <script src="{{ url_for('static', filename='js/vue.global.prod.js') }}"></script>
    <script src="{{ url_for('static', filename='js/index.full.min.js') }}"></script>
    <script src="{{ url_for('static', filename='js/index.iife.min.js') }}"></script>
    <script src="{{ url_for('static', filename='js/marked.min.js') }}"></script>
</head>
<body>
    <div id="app">
        <div class="chat-container">
            <div class="chat-header">
                <el-row :gutter="20">
                    <el-col :span="6">
                        <el-select v-model="selectedKnowledge" placeholder="选择知识库">
                            <el-option
                                v-for="item in knowledgeBases"
                                :key="item.id"
                                :label="item.name"
                                :value="item.id">
                            </el-option>
                        </el-select>
                    </el-col>
                    <el-col :span="6">
                        <el-select v-model="selectedModel" placeholder="选择模型">
                            <el-option
                                v-for="(item, index) in models"
                                :key="index"
                                :label="item"
                                :value="item">
                            </el-option>
                        </el-select>
                    </el-col>
                    <el-col :span="6">
                        <el-input-number 
                            v-model="topK" 
                            :min="1" 
                            :max="100"
                            placeholder="匹配文档数量"
                            :controls-position="'right'"
                            @change="handleTopKChange">
                        </el-input-number>
                    </el-col>
                    <el-col :span="6">
                        <el-button type="primary" @click="clearChat">清空对话</el-button>
                    </el-col>
                </el-row>
            </div>
            
            <div class="message-container" ref="messageContainer">
                <div v-for="(message, index) in messages" 
                     :key="index" 
                     :class="['message', message.role === 'user' ? 'user-message' : 'assistant-message']">
                    <el-avatar :icon="message.role === 'user' ? 'User' : 'Assistant'"></el-avatar>
                    <div class="message-content"
                         v-if="message.role === 'assistant' && message.isMarkdown"
                         v-html="message.content"></div>
                    <div class="message-content"
                         v-else
                         v-text="message.content"></div>
                </div>
            </div>
            
            <div class="input-area">
                <el-input
                    v-model="userInput"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入您的问题..."
                    @keyup.enter.ctrl="sendMessage">
                </el-input>
                <el-button 
                    type="primary" 
                    :loading="loading"
                    @click="sendMessage">发送</el-button>
                <el-button 
                    v-if="loading"
                    type="danger"
                    class="stop-generating"
                    @click="stopGenerating">停止生成</el-button>
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref, onMounted, nextTick, watch } = Vue
        const { ElMessage } = ElementPlus

        createApp({
            setup() {
                const messages = ref([])
                const userInput = ref('')
                const loading = ref(false)
                const selectedKnowledge = ref('')
                const selectedModel = ref('')
                const messageContainer = ref(null)
                
                const knowledgeBases = ref([])
                const models = ref([]) // 将硬编码的模型列表改为空数组
                const topK = ref(3) // 默认值为3
                const eventSource = ref(null)

                const loadKnowledgeBases = async () => {
                    try {
                        const response = await fetch('/api/knowledges')
                        const data = await response.json()
                        knowledgeBases.value = data
                    } catch (error) {
                        console.error('Failed to load knowledge bases:', error)
                    }
                }

                const loadModels = async () => {
                    try {
                        const response = await fetch('/api/models')
                        const data = await response.json()
                        if (Array.isArray(data)) {
                            models.value = data
                        } else {
                            ElMessage.error('获取模型列表失败')
                        }
                    } catch (error) {
                        console.error('Failed to load models:', error)
                        ElMessage.error('获取模型列表失败')
                    }
                }

                const scrollToBottom = () => {
                    nextTick(() => {
                        if (messageContainer.value) {
                            messageContainer.value.scrollTop = messageContainer.value.scrollHeight
                        }
                    })
                }

                const formatMessage = (content) => {
                    console.log('【formatMessage入参】', JSON.stringify(content))
                    marked.setOptions({
                        renderer: new marked.Renderer(),
                        highlight: function(code, language) {
                            return code;
                        },
                        pedantic: false,
                        gfm: true,
                        breaks: true, // 启用回车换行
                        sanitize: false,
                        smartLists: true,
                        smartypants: true,
                        xhtml: false
                    });
                    try {
                        // 预处理内容，保留回车
                        const processedContent = content
                            .replace(/^\s+|\s+$/g, '')
                            .replace(/\n\s*\n/g, '\n\n')
                            .replace(/\n\n\n+/g, '\n\n');
                        // 补全表格行的换行
                        let fixedContent = processedContent.replace(/(\|[^\n]*)\|/g, (match) => {
                            return match.endsWith('\n') ? match : match + '\n';
                        });
                        fixedContent = fixedContent.replace(/([^\n])(\| *-)/g, '$1\n$2');
                        console.log('【formatMessage预处理后】', JSON.stringify(fixedContent))
                        // 解析 Markdown
                        const html = marked.parse(fixedContent);
                        console.log('【formatMessage渲染后】', html)
                        return html;
                    } catch (e) {
                        console.error('Markdown parsing error:', e);
                        return content;
                    }
                }

                const appendAssistantMessage = (content, isNew = false) => {
                    // 替换后端 [[BR]] 为 \n
                    const plainContent = content.replaceAll('[[BR]]', '\n');
                    if (
                        messages.value.length > 0 &&
                        messages.value[messages.value.length - 1].role === 'assistant'
                    ) {
                        messages.value[messages.value.length - 1].content = plainContent;
                        messages.value[messages.value.length - 1].isMarkdown = false;
                    } else {
                        messages.value.push({
                            role: 'assistant',
                            content: plainContent,
                            isMarkdown: false
                        });
                    }
                    nextTick(() => {
                        scrollToBottom();
                    });
                }

                const stopGenerating = () => {
                    if (eventSource.value) {
                        eventSource.value.close()
                        eventSource.value = null
                        loading.value = false
                        if (messages.value.length > 0) {
                            const lastMessage = messages.value[messages.value.length - 1]
                            if (lastMessage.role === 'assistant') {
                                lastMessage.isTyping = false
                                // 流式结束后，将内容 markdown 渲染
                                // 再次替换，防止遗漏
                                lastMessage.content = formatMessage(lastMessage.content.replaceAll('[[BR]]', '\n'))
                                lastMessage.isMarkdown = true
                            }
                        }
                    }
                }

                const sendMessage = async () => {
                    if (!userInput.value.trim() || loading.value) return
                    if (!selectedKnowledge.value) {
                        ElMessage.warning('请选择知识库')
                        return
                    }

                    messages.value.push({
                        role: 'user',
                        content: userInput.value
                    })
                    
                    const question = userInput.value
                    userInput.value = ''
                    loading.value = true
                    scrollToBottom()

                    try {
                        if (eventSource.value) {
                            eventSource.value.close()
                        }

                        const params = new URLSearchParams({
                            question,
                            knowledge_id: selectedKnowledge.value,
                            model: selectedModel.value || 'llama2',
                            top_k: topK.value
                        })

                        eventSource.value = new EventSource(`/api/chat/stream?${params.toString()}`)
                        let currentResponse = ''

                        eventSource.value.onopen = () => {
                            console.log('SSE connection opened')
                        }

                        eventSource.value.onmessage = (event) => {
                            try {
                                console.log('【SSE原始内容】', JSON.stringify(event.data))
                                const token = event.data
                                if (token === '[DONE]') {
                                    stopGenerating()
                                    return
                                }
                                if (token.startsWith('Error:')) {
                                    throw new Error(token.substring(7))
                                }
                                // 累加内容
                                if (token === '\n') {
                                    currentResponse += '\n'
                                } else {
                                    currentResponse += token
                                }
                                console.log('【SSE累计内容】', JSON.stringify(currentResponse))
                                // 只更新最后一条 assistant 消消息
                                appendAssistantMessage(currentResponse)
                            } catch (error) {
                                console.error('Message processing error:', error)
                                ElMessage.error(error.message || '处理消息时发生错误')
                                stopGenerating()
                            }
                        }

                        eventSource.value.onerror = (error) => {
                            console.error('EventSource error:', error)
                            stopGenerating()
                            if (!currentResponse) {
                                ElMessage.error('连接服务器失败，请稍后重试')
                            }
                        }

                    } catch (error) {
                        console.error('Send message error:', error)
                        ElMessage.error('发送消息失败')
                        loading.value = false
                    }
                }

                const clearChat = () => {
                    messages.value = []
                    if (eventSource.value) {
                        eventSource.value.close()
                        eventSource.value = null
                    }
                }

                const handleTopKChange = (value) => {
                    console.log('Top K changed:', value)
                }

                onMounted(() => {
                    loadKnowledgeBases()
                    loadModels()
                })

                const selectedKnowledgeChanged = async (newValue) => {
                    if (newValue) {
                        await loadModels()
                    }
                }

                watch(selectedKnowledge, selectedKnowledgeChanged)

                return {
                    messages,
                    userInput,
                    loading,
                    selectedKnowledge,
                    selectedModel,
                    knowledgeBases,
                    models,
                    messageContainer,
                    sendMessage,
                    clearChat,
                    formatMessage,
                    loadModels,
                    topK,
                    handleTopKChange,
                    stopGenerating
                }
            }
        }).use(ElementPlus).mount('#app')
    </script>
</body>
</html>